# cython: profile=False
# -*- coding: utf-8 -*-
#*****************************************************************************
#  Copyright (C) 2010 Fredrik StrÃ¶mberg <stroemberg@mathematik.tu-darmstadt.de>,
#
#  Distributed under the terms of the GNU General Public License (GPL)
#
#    This code is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    General Public License for more details.
#
#  The full text of the GPL is available at:
#
#                  http://www.gnu.org/licenses/
#****************************************************************************

r"""
Algorithms and classes for permutations representing subgroups of the modular group, as implemented in 'MySubgroup'.

CLASSES:


AUTHOR:

 - Stephan Ehlen
 - 


"""

from sage.modules.vector_integer_dense import *
from sage.misc.functional import is_even
from sage.rings.arith import kronecker,odd_part,gcd,valuation,is_prime
from sage.rings.integer import Integer
from sage.rings.finite_rings.constructor import FiniteField

#cdef int ngens
#cdef int* gen_orders

cpdef cython_el_index(c, gen_orders):
    cdef long ii, jj = 0
    cdef md = 1
    ii=0
    md=1
    for jj in range(0,len(gen_orders)):
        m=gen_orders[jj]
        #print jj,n,c[jj]
        ii=ii+(c[jj]%int(m))*md
        md=md*m
    return ii


cpdef cython_elt(long ii,gen_orders):
    elt=list()
    cdef long md = 1
    cdef long jj = 0
    for jj in range(0,len(gen_orders)):
        md=gen_orders[jj]
        c=ii%md
        elt.append(c)
        ii=ii-c
        ii=ii
        ii=ii/md
    return elt

cpdef cython_neg_index(long ii, gen_orders):
    cdef long jj=0
    elt = cython_elt(ii,gen_orders)
    for jj in range(0,len(gen_orders)):
        elt[jj]=-elt[jj]
    return cython_el_index(elt,gen_orders)

cpdef cython_neg_indices(long n, gen_orders):
    cdef long jj=0
    l=list()
    for jj in range(0,n):
        l.append(cython_neg_index(jj,gen_orders))
    return l

cpdef cython_xis(int a, int b, int c, int d, W):
    cdef int dc=1
    cdef int argl=0
    JD=W._QM.jordan_decomposition()
    cdef long absD = W._n
    cdef long p = 0
    oddity=W._inv['total oddity']
    oddities=W._inv['oddity']
    pexcesses=W._inv['p-excess']
    sign=W._inv['signature']
    z8=W._z8
    gammafactor=1
    xis=dict()
    for comp in JD:
        p=comp[1 ][0 ]
        xis[p]=1 
    if(a*d <>0 ):
        if(is_even(sign)):
            argl=- 2 *sign
            xis[0 ]=z8**argl
        else:
            dc=kronecker(-a,c)
            argl=-2 *sign
            if(is_even(c)):
                argl=argl+(a+1 )*(odd_part(c)+1 )
            xis[0 ]=z8**argl*dc
    else:
        argl=-sign
        xis[0 ]=z8**argl
        return xis
    if(xis.keys().count(2 )>0 ):
        if(is_odd(c)):
            argl=(c*oddity) % 8 
        else:
            argl=(-(1 +a)*oddity) % 8 
        xis[2]=z8**argl
    for comp in JD:
        [p,n,r,ep]=comp[1 ][:4 ]
        t = None if (len(comp[1 ])==4 ) else comp[1 ][4 ]
        q=p**n
        qc=gcd(q,c)
        qqc=Integer(q/qc)
        ccq=Integer(c/qc)
        nq=valuation(qqc,p)
        gammaf=1 
        dc=1 
        #if(c % q == 0): # This only contributes a trivial factor
        #    continue
        if(p==2 ):
            if(is_even(c)):
                if(c % q<>0 ):
                    odt=self._get_oddity(p,nq,r,ep,t)
                    argl=-a*ccq*odt
                    gammaf=z8**argl
                    dc=kronecker(-a*ccq,qqc**r)
                dc=dc*kronecker(-a,q**r)
                xis[ 2 ]=xis[ 2 ]*gammaf*dc
            else:
                dc=kronecker(c,q**r)
                xis[ 2 ]=xis[ 2 ]*dc
        else:
            if(c%q <>0 ):
                exc=W._get_pexcess(p,nq,r,ep)
                argl=-exc
                gammaf=z8**argl
                dc=kronecker(ccq,qqc**r)
            dc=dc*kronecker(-d,qc**r)
            xis[p]=xis[p]*gammaf*dc
        return xis

cpdef cython_get_gammaN_conjcl_reps(long N):
#    matrix=matrix22()
    if not is_prime(N) and N>2:
        raise NotImplementedError()
    #n=self._QM.order()
    cdef int p=N
    cdef int eps = 0
    F=FiniteField(p)
    four = F(4)
    if p == 3:
        eps = 2
    for ii in range(0,(p-1)/2+1):
        print ii, kronecker(ii,p)
        if ii != 0 and kronecker(ii,p)==-1:
            eps=ii
            print eps, kronecker(ii,p)
            break
    cl=list()
    cl.append([[1,0,0,1],1])
    cl.append([[-1,0,0,-1],1])
    cl.append([[1,1,0,1],(p-1)*(p+1)/2])
    cl.append([[-1,1,0,-1],(p-1)*(p+1)/2])
    cl.append([[1,eps,0,1],(p-1)*(p+1)/2])
    cl.append([[-1,eps,0,-1],(p-1)*(p+1)/2])
    done=list()
    if kronecker(-1,p)==-1:
        cl.append([[0,-1,1,0],p*(p-1)])
    for a in F:
        if a != 0 and a != 1 and a != -1:
            m=[a.lift(),0,0,(a**-1).lift()]
            c=p*(p+1)
            mp=[m,c]
            if not (a**-1) in done:
                cl.append(mp)
                done.append(a)
        x = a**2-four
        if a!= 0 and kronecker(x,p)==-1:
            cl.append([[0,-1,1,a.lift()],p*(p-1)])
    return cl
